package com.ouc.pampas;

import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.common.collect.Maps;
import io.terminus.pampas.common.Response;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class AppServiceImpl implements AppService {
	private final AppDao appDao;
	private final ConfigManager configManager;
	private final Setting setting;
	private final LoadingCache<String, Optional<App>> domainAppCache;

	public List<App> listAll() {
		return this.configManager.listAllApp();
	}

	public Map<String, Object> listAllWithSetting() {
		Map<String, Object> result = Maps.newHashMap();
		result.put("apps", listAll());
		result.put("implant", Boolean.valueOf(this.setting.getMode() == Setting.Mode.IMPLANT));
		if (this.setting.getMode() == Setting.Mode.CENTER) {
			result.put("rootPath", this.setting.getRootPath());
		}
		return result;
	}

	@SuppressWarnings("unchecked")
	@Autowired
	public AppServiceImpl(final AppDao appDao, ConfigManager configManager, Setting setting) {
		this.appDao = appDao;
		this.configManager = configManager;
		this.setting = setting;
		if (!setting.isDevMode()) {
			this.domainAppCache = CacheBuilder.newBuilder().build(new CacheLoader() {
				public Optional<App> load(Object domain) throws Exception {
					return Optional.fromNullable(appDao.findByDomain((String)domain));
				}
			});
		} else {
			this.domainAppCache = null;
		}
	}

	public App findByDomain(String domain) {
		if (this.domainAppCache == null) {
			return this.appDao.findByDomain(domain);
		}
		Optional<App> optionalResult = (Optional) this.domainAppCache.getUnchecked(domain);
		return (App) optionalResult.orNull();
	}

	public App findByKey(String key) {
		return this.appDao.findByKey(key);
	}

	public Response<String> create(App app) {
		Preconditions.checkNotNull(app.getKey());
		Preconditions.checkNotNull(app.getDomain());
		if (this.appDao.findByKey(app.getKey()) != null) {
			return Response.fail("app with key [" + app.getKey() + "] is already exists");
		}
		if (this.appDao.findByDomain(app.getDomain()) != null) {
			return Response.fail("app with domain [" + app.getDomain() + "] is already exists");
		}
		this.appDao.create(app);
		return Response.ok(app.getKey());
	}

	public Response<Void> update(App app) {
		Preconditions.checkNotNull(app.getKey());
		if (!Strings.isNullOrEmpty(app.getDomain())) {
			App exists = this.appDao.findByDomain(app.getDomain());
			if ((exists != null) && (!Objects.equal(app.getKey(), exists.getKey()))) {
				return Response.fail("app with domain [" + app.getDomain() + "] is already exists");
			}
		}
		this.appDao.update(app);
		return Response.ok();
	}

	public Response<Void> updateExtraDomains(String key, Set<String> extraDomains) {
		Preconditions.checkNotNull(key);
		Preconditions.checkNotNull(extraDomains);
		for (String domain : extraDomains) {
			App exists = this.appDao.findByDomain(domain);
			if ((exists != null) && (!Objects.equal(key, exists.getKey()))) {
				return Response.fail("app with domain [" + domain + "] is already exists");
			}
		}
		this.appDao.updateExtraDomains(key, extraDomains);
		return Response.ok();
	}

	public Set<String> getExtraDomains(String key) {
		Preconditions.checkNotNull(key);
		return this.appDao.getExtraDomains(key);
	}

	public void delete(String key) {
		Preconditions.checkNotNull(key);
		this.appDao.delete(key);
	}
}
